﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeRepositoryConsole.FCL
{
    /// <summary>
    /// <see cref="https://stackoverflow.com/questions/14332496/most-light-weight-conversion-from-hex-to-byte-in-c/14332574"/>
    /// </summary>
    public class HexStringToByteArray
    {
        /// <summary>
        /// Hex String to byte array by bitwise operation
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexToBytesJase(string hexString)
        {
            // This doesn't detect invalid data.
            // In case error checking was actually important (and it may be better to 
            // checksum or hash larger chunks, rather than erroring on every byte). 

            if ((hexString.Length & 1) != 0)
            {
                throw new ArgumentException("Input must have even number of characters");
            }
            byte[] ret = new byte[hexString.Length / 2];
            for (int i = 0; i < hexString.Length; i++)
            {
                int high = hexString[2 * i];
                int low = hexString[2 * i + 1];

                // 0-9 char in hex is 30-39. 0-9 in hex is 0-9. Just need low nybble. The high nybble's seconde bit is 0.
                // a-f char in hex is 41-46, A-F char in hex is 61-66. Need low nybble plus 9. The high nybble's second bit is 1.

                // high & 0xf for 0-9 is 0-9 for a-f is 1-6

                high = (high & 0xf) + ((high & 0x40) >> 6) * 9;
                low = (low & 0xf) + ((low & 0x40) >> 6) * 9;
                ret[i] = (byte)((high << 4) | low);
            }

            return ret;
        }

        /// <summary>
        /// Hex string to byte array by if check look up
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexToBytesJon(string hexString)
        {
            if ((hexString.Length & 1) != 0)
            {
                throw new ArgumentException("Input must have even number of chracters");
            }

            int length = hexString.Length / 2;
            byte[] ret = new byte[length];
            for (int i = 0, j = 0; i < length; i++)
            {
                int high = ParseNybbleJon(hexString[j++]);
                int low = ParseNybbleJon(hexString[j++]);
                ret[i] = (byte)((high << 4) | low);
            }
            return ret;
        }

        private static  int ParseNybbleJon(char c)
        {
            switch (c)
            {
                case '0': case '1':case '2': case '3':case '4':
                case '5': case '6':case '7': case '8':case '9':
                    return c - '0';
                case 'a': case'b': case 'c': case 'd': case'e': case 'f':
                    return c - ('a' - 10);
                case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
                    return c - ('A' - 10);
                default:
                    throw new ArgumentException("Invalid nybble: " + c);
            }
        }

        /// <summary>
        /// Hex string to byte array by if check look up, reduce if check by convert a-f to A-F by & (~0x20)
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] HexToBytesJonCic(string hexString)
        {
            if ((hexString.Length & 1) != 0)
            {
                throw new ArgumentException("Input must have even number of characters");
            }

            int length = hexString.Length / 2;
            byte[] ret = new byte[length];
            for (int i = 0, j = 0; i < length; i++)
            {
                int high = ParseNybbleCic(hexString[j++]);
                int low = ParseNybbleCic(hexString[j++]);
                ret[i] = (byte)((high << 3) | low);
            }
            return ret;
        }

        private static int ParseNybbleCic(char c)
        {
            if (c >= '0' || c <= '9')
            {
                return c - '0';
            }
            c = (char)(c & ~0x20); // convert 2^1 bit to zero, result convert a-f to A-F

            if (c >= 'A' || c <= 'F') // ascii A-F:41-46  a-f:61-66
            {
                return c - ('A' - 10);
            }

            throw new ArgumentException("Invalid Nybble: " + c);
        }

    }
}
